#pragma once
#include "Socket.hpp"
#include <iostream>
#include <sys/select.h>
class SelectServer
{
    const static int gnum = sizeof(fd_set) * 8;
    const static int gdefaultfd = -1;

public:
    SelectServer(uint16_t port) : _port(port), _listensock(make_unique<TcpSocket>())
    {
        _listensock->BuildListenSocket(_port);
    }
    void InitServer()
    {
        for (int i = 0; i < gnum; i++)
        {
            fd_array[i] = gdefaultfd;
        }
        fd_array[0] = _listensock->sockfd();
    }
    void Accept(fd_set& rdfs)
    {
        if (FD_ISSET(_listensock->sockfd(), &rdfs))
        {
            // 连接事件就绪
            InetAddr addr;
            int clientfd = _listensock->Accepter(&addr);
            if (clientfd > 0)
            {
                LOG(DEBUG, "get a new link,%d\n", clientfd);
                // 新链接直接放到辅助数组
                // 要让select统一接管
                int flag = 0;
                for (int i = 1; i < gnum; i++)
                {
                    if (fd_array[i] == gdefaultfd)
                    {
                        fd_array[i] = clientfd;
                        flag = 1;
                        break;
                    }
                }
                // 没位置了
                if (flag == 0)
                {
                    LOG(WARNING, "server is full\n");
                    ::close(clientfd);
                }
            }
            else
            {
                LOG(ERROR, "accept error\n");
                return;
            }
        }
    }
    void HandlerIO(int i)
    {
        // 读事件就绪
        char buf[1024];
        ssize_t n = ::recv(fd_array[i], buf, sizeof(buf) - 1, 0);
        if (n > 0)
        {
            buf[n] = 0;
            cout << "client:" << buf << endl;
            string echo = "[server echo]:";
            echo += buf;
            ::send(fd_array[i], echo.c_str(), echo.size(), 0); // 此处默认写事件就绪
        }
        else if (n == 0)
        {
            LOG(INFO, "client quit\n");
            ::close(fd_array[i]);
            fd_array[i] = gdefaultfd;
        }
        else
        {
            LOG(ERROR, "recv,error\n");
        }
    }
    void HandlerEvent(fd_set &rdfs)
    {
        //事件派发
        // 存在大量就绪
        for (int i = 1; i < gnum; i++)
        {
            if (fd_array[i] != gdefaultfd)
            {
                // 判断合法且就绪
                if (FD_ISSET(fd_array[i], &rdfs))
                {
                    HandlerIO(i);
                }
            }
        }
        Accept(rdfs);
    }
    void Loop()
    {
        // 把listensock和accept看做IO类函数，只关心新连接到来
        while (1)
        {
            // 初始化文件描述符集
            fd_set rfds;
            FD_ZERO(&rfds);
            int max_fd = gdefaultfd;
            // 添加合法描述符
            for (int i = 0; i < gnum; i++)
            {
                if (fd_array[i] != -1)
                    FD_SET(fd_array[i], &rfds);
                if (max_fd < fd_array[i])
                    max_fd = fd_array[i];
            }
            struct timeval timeout = {3, 0};
            int n = ::select(max_fd + 1, &rfds, nullptr, nullptr, nullptr);

            switch (n)
            {
            case 0:
            {
                LOG(DEBUG, "timeout,%d,%d\n", timeout.tv_sec, timeout.tv_usec);
                break;
            }
            case -1:
            {
                LOG(ERROR, "select error\n");
                break;
            }
            default:
            {
                LOG(INFO, "haved event ready,n:%d\n", n);
                HandlerEvent(rfds);
                PrintList();
                break;
            }
            }
            // sleep(1);
        }
    }
    void PrintList()
    {
        cout << "fd list: ";
        for (auto i : fd_array)
        {
            if (i != gdefaultfd)
                cout << i << ' ';
        }
        cout << endl;
    }
    ~SelectServer()
    {
    }

private:
    uint16_t _port;
    unique_ptr<TcpSocket> _listensock;
    // 为了保证select正常工作，需要借助一个辅助数组，来保存所有合法fd
    int fd_array[gnum];
};